package com.fly.core.aop;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import com.fly.core.annotation.OnceSchedule;
import com.fly.core.utils.SpringContextUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Aspect
@Component
public class OnceScheduleAspect
{
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    private String key = "job001";
    
    private String profile;
    
    /**
     * 初始化
     */
    @PostConstruct
    public void init()
    {
        profile = SpringContextUtils.getActiveProfile();
        long count = jdbcTemplate.queryForObject("SELECT count(*) FROM t_schedule_lock WHERE key_name=?", Long.class, key);
        if (count == 0)
        {
            jdbcTemplate.update("INSERT INTO t_schedule_lock(key_name, time) VALUES(?, now())", key);
        }
    }
    
    /**
     * 集群环境定时任务执行1次
     * 
     * @param joinPoint
     * @param scheduled
     * @return
     * @throws Throwable
     */
    @Around("@annotation(onceSchedule)")
    public Object around(ProceedingJoinPoint joinPoint, OnceSchedule onceSchedule)
        throws Throwable
    {
        Assert.isTrue(onceSchedule.nextSeconds() > 0, "nextSeconds value error");
        if (getLock(onceSchedule.nextSeconds()))
        {
            return joinPoint.proceed();
        }
        log.info("scheduled ignored");
        return null;
    }
    
    /**
     * 获取lock,并更新time为下次执行时间 now()+ seconds<BR>
     * 
     * 注意: 为了保证jdbcTemplate.update执行成功，一般设置seconds稍小于@Scheduled设置的运行间隔秒数
     * 
     * @return
     */
    private boolean getLock(long seconds)
    {
        int count = 0;
        switch (profile)
        {
            case "dev": // H2
                String timeStr = DateFormatUtils.format(System.currentTimeMillis() + seconds * 1000L, "yyyy-MM-dd HH:mm:ss");
                count = jdbcTemplate.update("UPDATE t_schedule_lock SET time = ? WHERE key_name = ? AND now() > time", timeStr, key);
                break;
            
            case "test": // mysql
            case "prod": // mysql
                count = jdbcTemplate.update("UPDATE t_schedule_lock SET time = adddate(now(), INTERVAL ? SECOND) WHERE key_name = ? AND now() > time", seconds, key);
                break;
            
            default:
                break;
        }
        return count > 0;
    }
}
